﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TheWorldsWorst.ApiWrapper;
using TheWorldsWorst.ApiWrapper.Model;

namespace TheWorldsWorst.StackOverflowClone.Controllers
{
    /// <summary>
    /// One big ol' Controller to route everything throughs
    /// </summary>
    [HandleError]
    public class HomeController : Controller
    {
        enum QuestionSortOrder
        {
            newest,
            hot,
            featured,
            active
        }

        enum UnansweredSortOrder
        {
            newest,
            votes
        }

        enum TagSortOrder
        {
            name,
            popular
        }

        enum UserSortOrder
        {
            reputation,
            newest,
            oldest,
            name
        }

        private T ParseSortOrder<T>(string val, T def) where T : struct
        {
            try
            {
                return (T)Enum.Parse(typeof(T), val);
            }
            catch
            { }

            return def;
        }

        public ActionResult Question(int id, int? pageSize, int? pageNum)
        {
            var pS = pageSize ?? 15;
            var pN = pageNum ?? 1;

            var q = ApiProxy.GetQuestionFor(id, pN, pS);

            ViewData["PageNum"] = pN;
            ViewData["PageSize"] = pS;

            return View(q);
        }

        public ActionResult Index(string sortOrder, int? pageNum, int? pageSize)
        {
            IEnumerable<Question> questions = null;

            var sort = ParseSortOrder(sortOrder, QuestionSortOrder.active);

            switch (sort)
            {
                case QuestionSortOrder.active: questions = ApiProxy.GetActiveQuestions(pageNum.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                case QuestionSortOrder.featured: questions = ApiProxy.GetFeaturedQuestions(pageNum.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                case QuestionSortOrder.hot: questions = ApiProxy.GetHotQuestions(pageNum.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                case QuestionSortOrder.newest: questions = ApiProxy.GetNewestQuestions(pageNum.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                default: throw new Exception("Invalid sort order");
            }

            ViewData["SortOrder"] = Enum.GetName(typeof(QuestionSortOrder), sort);
            ViewData["PageNum"] = pageNum.GetValueOrDefault(1);
            ViewData["PageSize"] = pageSize.GetValueOrDefault(50);

            return View(questions);
        }

        public ActionResult Tags(string id, int? pageNum, int? pageSize)
        {
            IEnumerable<Question> questions = ApiProxy.GetNewestQuestionsWithTag(id, pageNum.GetValueOrDefault(1), pageSize.GetValueOrDefault(50));
           
            ViewData["Tag"] = id;
            ViewData["PageNum"] = pageNum.GetValueOrDefault(1);
            ViewData["PageSize"] = pageSize.GetValueOrDefault(50);
            
            return View(questions);
        }

        public ActionResult Badges(int id)
        {
            var badge = ApiProxy.GetBadgeFor(id);

            return View(badge);
        }

        public ActionResult Unanswered(string sortOrder, int? pageNum, int? pageSize)
        {
            IEnumerable<Question> questions = null;

            var sort = ParseSortOrder(sortOrder, UnansweredSortOrder.newest);

            switch (sort)
            {
                case UnansweredSortOrder.votes: questions = ApiProxy.GetUnansweredQuestionsByVotes(pageNum.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                case UnansweredSortOrder.newest: questions = ApiProxy.GetUnansweredQuestionsByAge(pageNum.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                default: throw new Exception("Invalid sort order");
            }

            ViewData["SortOrder"] = Enum.GetName(typeof(UnansweredSortOrder), sort);
            ViewData["PageNum"] = pageNum.GetValueOrDefault(1);
            ViewData["PageSize"] = pageSize.GetValueOrDefault(50);

            return View(questions);
        }

        public ActionResult Timeline(int id)
        {
            var events = ApiProxy.GetQuestionTimeline(id);

            ViewData["QuestionId"] = id;

            return View(events);
        }

        public ActionResult UserActivity(int id)
        {
            var events = ApiProxy.GetUserEvents(id, DateTime.Now - new TimeSpan(31, 0, 0, 0), DateTime.Now);

            ViewData["UserName"] = ApiProxy.GetUserFor(id).Name;
            ViewData["UserId"] = id;

            return View(events);
        }

        public ActionResult ListTags(string sortorder, string filter, int? pageNum, int? pageSize)
        {
            TagSortOrder sort = ParseSortOrder(sortorder, TagSortOrder.popular);
            IEnumerable<Tag> tags = null;

            switch (sort)
            {
                case TagSortOrder.name: tags = ApiProxy.GetTagsByName(pageNum.GetValueOrDefault(1), pageSize.GetValueOrDefault(50), filter); break;
                case TagSortOrder.popular: tags = ApiProxy.GetTagsByPopularity(pageNum.GetValueOrDefault(1), pageSize.GetValueOrDefault(50), filter); break;
                default: throw new Exception("Invalid sort order");
            }

            ViewData["SortOrder"] = Enum.GetName(typeof(TagSortOrder), sort);
            ViewData["PageNum"] = pageNum.GetValueOrDefault(1);
            ViewData["PageSize"] = pageSize.GetValueOrDefault(50);
            ViewData["Filter"] = filter;

            return View(tags);
        }

        public ActionResult PostRedirect(int id)
        {
            try
            {
                ApiProxy.GetQuestionFor(id);
                return Redirect(String.Format("/question/{0}", id));
            }
            catch { }

            try
            {
                var ans = ApiProxy.GetAnswerFor(id);
                return Redirect(String.Format("/question/{0}#{1}", ans.QuestionId, ans.AnswerId));
            }
            catch { }

            //Will only happen if a post has gone "missing" between two API requests
            //  ie. from deletion or the like
            throw new Exception("No Such Id");
        }

        public ActionResult CommentRedirect(long id)
        {
            return View();
        }

        public ActionResult Deck(int? userId, string remove, string since, string to)
        {
            var list = Session["DeckList"] as List<int>;

            if (list == null) { list = new List<int>(); Session["DeckList"] = list; }

            if (string.IsNullOrEmpty(remove))
            {
                if (userId.HasValue)
                    if (!list.Contains(userId.Value))
                        list.Add(userId.Value);
            }
            else
            {
                if(userId.HasValue)
                    list.Remove(userId.Value);
            }

            DateTime from, toDate;


            if (!String.IsNullOrEmpty(to))
            {
                if (!DateTime.TryParse(to, out toDate)) toDate = DateTime.Now;
            }
            else
            {
                toDate = DateTime.Now;
            }

            if (!String.IsNullOrEmpty(since))
            {
                if (!DateTime.TryParse(since, out from)) from = toDate - new TimeSpan(7, 0, 0, 0);
            }
            else
            {
                from = toDate - new TimeSpan(7, 0, 0, 0);
            }

            var events = new List<object>();

            foreach (var i in list)
            {
                events.AddRange(ApiProxy.GetUserEvents(i, from, toDate).Cast<object>());
                events.AddRange(ApiProxy.GetUserMentions(i, from, toDate).Cast<object>());


                var favorites = ApiProxy.GetUserFavorites(i, from, toDate);
                foreach (var f in favorites)
                {
                    events.AddRange(ApiProxy.GetQuestionEvents(f, from, toDate).Cast<object>());
                }
            }

            events.Sort(
                delegate(object e1, object e2)
                {
                    DateTime first, second;

                    if (e1 is ATimelineEvent) first = ((ATimelineEvent)e1).CreationDate; else first = ((Comment)e1).CreationDate;
                    if (e2 is ATimelineEvent) second = ((ATimelineEvent)e2).CreationDate; else second = ((Comment)e2).CreationDate;

                    //Descending order
                    return first.CompareTo(second) * -1;
                });

            var users = list.Select(p => new UserPair { Id = p, Name = ApiProxy.GetUserFor(p).Name });

            ViewData["Users"] = users;
            ViewData["Since"] = from.ToString("dd/MM/yyyy");
            ViewData["To"] = toDate.ToString("dd/MM/yyyy");

            return View(events);
        }

        public ActionResult Users(int? id, string filter, string tab, int? pageSize, int? page)
        {
            if (id == null)
                return ManyUsers(filter, tab, pageSize, page);
            else
                return SingleUser(id.Value);
        }
        private ActionResult SingleUser(int id)
        {
            var u = ApiProxy.GetUserFor(id);

            return View("Users", u);
        }
        private ActionResult ManyUsers(string filter, string tab, int? pageSize, int? page)
        {
            IEnumerable<User> users;

            UserSortOrder sort = ParseSortOrder(tab, UserSortOrder.reputation);

            switch (sort)
            {
                case UserSortOrder.reputation: users = ApiProxy.GetUsersByReputation(filter, page.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                case UserSortOrder.name: users = ApiProxy.GetUsersByName(filter, page.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                case UserSortOrder.newest: users = ApiProxy.GetNewestUsers(filter, page.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                case UserSortOrder.oldest: users = ApiProxy.GetOldestUsers(filter, page.GetValueOrDefault(1), pageSize.GetValueOrDefault(50)); break;
                default: throw new Exception("Unexpected UserSortOrder");
            }

            ViewData["Filter"] = filter;
            ViewData["Tab"] = Enum.GetName(typeof(UserSortOrder), sort);
            ViewData["PageSize"] = pageSize.GetValueOrDefault(50);
            ViewData["Page"] = page.GetValueOrDefault(1);

            // REVIEW: Future: page count not yet supported
            ViewData["PageMax"] = "?";

            return View("FindUsers", users);
        }

        public ActionResult RepGraph(int id, string from, string to)
        {
            DateTime dtFrom, dtTo;

            if(!DateTime.TryParse(to, out dtTo)) dtTo = DateTime.Now;
            if(!DateTime.TryParse(from, out dtFrom)) dtFrom = dtTo - new TimeSpan(31, 0, 0, 0);

            var rep = ApiProxy.GetReputationGraphFor(id, dtFrom, dtTo, 1, 50);

            ViewData["UserId"] = id;
            ViewData["From"] = dtFrom.ToString("dd/MM/yyyy");
            ViewData["To"] = dtTo.ToString("dd/MM/yyyy");

            return View(rep);
        }
    }

    /// <summary>
    /// Storage class for Deck user data
    /// </summary>
    public class UserPair
    {
        public int Id;
        public string Name;

        public override bool Equals(object obj)
        {
            if (!(obj is UserPair)) return false;

            var other = obj as UserPair;

            return other.Id == Id && other.Name.Equals(Name);
        }

        public override int GetHashCode()
        {
            return Id.GetHashCode() ^ Name.GetHashCode();
        }
    }
}
